﻿////////////////////////////////////////////////////////////////
///
/// File: ComplexTmpExp.cs
/// Author: Lucio Davide Spano
/// Last update: 27 July 2012
/// Description: This file contains the definition of a n-ary
///              temporal expression 
/// To do: 
/// ------------------------------------------------------------
/// Copyright (c) 2012 Lucio Davide Spano 
///                     (lucio.davide.spano@isti.cnr.it)
/// 
/// The use and distribution terms for this software are 
/// contained in the file named license.txt, which can be found 
/// in the root of this distribution.
/// By using this software in any fashion, you are agreeing to 
/// be bound by the terms of this license.
///
/// You must not remove this notice, or any other, from this
/// software.
/// ------------------------------------------------------------
////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GestIT
{
    public class ComplexTmpExp : TmpExp
    {
        public TmpOperator Operator { get; set; }
        private List<TmpExp> operands;
        private List<bool> completion;


        public ComplexTmpExp()
        {
            this.Operator = TmpOperator.OrderIndependence;
            this.operands = new List<TmpExp>();
            this.completion = new List<bool>();
            this.type = ExpType.Complex;
        }

        public void AddOperand(TmpExp operand)
        {
            this.operands.Add(operand);
            this.completion.Add(false);
            operand.Parent = this;
            operand.OnStateChanged += new StateChangedHandler(operand_OnStateChanged);
            operand.OnNotAcceptedToken += new NotAcceptedToken(operand_OnNotAcceptedToken);
        }

       



        public void RemoveOperand(TmpExp operand)
        {
            int position = FindOperatorPosition(operand);
            if (position == -1)
            {
                return;
            }
            this.operands.Remove(operand);
            this.completion.RemoveAt(position);
            operand.OnStateChanged -= new StateChangedHandler(operand_OnStateChanged);
            operand.OnNotAcceptedToken -= new NotAcceptedToken(operand_OnNotAcceptedToken);
        }

        public IEnumerable<TmpExp> GetOperands()
        {
            foreach (TmpExp op in operands)
            {
                yield return op;
            }
        }

        public TmpExp GetOperand(int pos)
        {
            if (pos >= 0 && pos < operands.Count)
            {
                return operands.ElementAt(pos);
            }
            return null;
        }

        private int FindOperatorPosition(TmpExp exp)
        {
            int i = 0;
            foreach (TmpExp op in operands)
            {
                if (op == exp)
                {
                    return i;
                }
                i++;
            }
            return -1;
        }

        public override void SetExectState(ExecState state, Token token)
        {
            switch (Operator)
            {
                case TmpOperator.OrderIndependence:
                    switch (state)
                    {
                        case ExecState.Enabled:
                            if (this.State == ExecState.Disabled || this.State == ExecState.Performing)
                            {
                                for (int i = 0; i < completion.Count; i++)
                                {
                                    completion[i] = false;
                                }
                            }
                            foreach (TmpExp op in operands)
                            {
                                if (op.State == ExecState.Disabled || op.State == ExecState.Completed || op.State == ExecState.Error)
                                {
                                    op.SetExectState(ExecState.Enabled, token);
                                }
                            }
                            break;

                        case ExecState.Performing:
                            // nothing to do 
                            break;
                        case ExecState.Disabled:
                            foreach (TmpExp op in operands)
                            {
                                op.SetExectState(ExecState.Disabled, token);
                            }
                            break;

                        case ExecState.Completed:
                            if (this.Iterative)
                            {
                                this.SetExectState(ExecState.Enabled, token);
                            } 
                            break;
                    }
                    break;

                default: break;
                // the remaining operators are covered by te BinaryTmpExp class
            }
            base.SetExectState(state, token);

        }

        public override bool Accepts(ExpEventArgs args)
        {
            foreach(TmpExp op in this.operands)
            {
                if ((op.State == ExecState.Enabled || op.State == ExecState.Performing)
                    && op.Accepts(args))
                {
                    return true;
                }
            }
            return false;
        }

        void operand_OnStateChanged(TmpExp sender, ExpEventArgs args, Token token)
        {
            this.SetExectState(sender.State, token);
            switch (sender.State)
            {
                case ExecState.Completed:
                    {
                        int pos = FindOperatorPosition(sender);
                        if (pos >= 0)
                        {
                            this.completion[pos] = true;

                            bool allDone = true;
                            for (int i = 0; i < completion.Count; i++)
                            {
                                allDone = allDone && completion[i];
                            }
                            if (allDone)
                            {
                                this.SetExectState(ExecState.Completed, token);
                                RaiseCompleted(this, args, token);
                                RaiseOnStateChanged(this, args, token);
                            }
                            else
                            {
                                this.SetExectState(ExecState.Performing, token);
                                RaisePerforming(this, args, token);
                                RaiseOnStateChanged(this, args, token);
                            }
                        }
                    }
                    break;

                case ExecState.Performing:
                    {
                        int pos = FindOperatorPosition(sender);
                        if (pos >= 0)
                        {
                            for (int i = 0; i < operands.Count; i++)
                            {
                                // Disable the other operands if the
                                // user started to execute one operand
                                if (!completion[i])
                                {
                                    operands[i].SetExectState(ExecState.Disabled, token);
                                }
                            }
                            this.SetExectState(ExecState.Performing, token);
                            RaisePerforming(this, args, token);
                            RaiseOnStateChanged(this, args, token);
                        }

                    }
                    break;

                case ExecState.Error:
                    this.SetExectState(ExecState.Error, token);
                    RaiseError(this, args, token);
                    RaiseOnStateChanged(this, args, token);
                    if (this.Iterative && token != null)
                    {
                        // avoid enabling null tokens
                        this.SetExectState(ExecState.Enabled, token);
                    }
                    break;
            }
        }

        void operand_OnNotAcceptedToken(TmpExp sender, SimpleTmpExp originalSender, ExpEventArgs args, Token token)
        {
            this.RaiseOnNotAcceptedToken(originalSender, args, token);
        }
    }
}
